Frontend Forever App
We have a mobile app for you to download and use. And you can unlock many features in the app.
Get it now
Intall Later
Run
HTML
CSS
Javascript
Output
Document
Select Audio
Reset Simulation
Save Weights
Load Weights
Toggle Debug
@charset "UTF-8"; @import url(https://fonts.googleapis.com/css?family=Nunito+Sans:300,400,600,700,800); *, :after, :before { box-sizing: border-box; padding: 0; margin: 0; } @import url(https://fonts.googleapis.com/css?family=VT323); body { margin: 0; overflow: hidden; font-family: "VT323"; display: flex; align-items: center; justify-content: center; height: 100vh; background: black; flex-direction: column; } #container { display: grid; color: #0f0; flex: 1; width: 100%; } .controls { position: absolute; bottom: 10px; left: 50%; transform: translateX(-50%); display: flex; gap: 10px; } #audioFileInput { opacity: 0; position: absolute; z-index: -1; } label[for="audioFileInput"], label[for="loadWeightsInput"], button { padding: 10px 20px; background-color: black; color: white; border: none; border-radius: 5px; font-size: 16px; cursor: pointer; margin: 0; font-family: "VT323"; } label[for="audioFileInput"]:hover, label[for="loadWeightsInput"]:hover, button:hover { color: #0f0; } #debugPanel { position: absolute; top: 10px; right: 10px; width: max-content; background: rgba(0, 0, 0, 0.1); color: #0f0; border-radius: 10px; padding: 10px; display: none; backdrop-filter: blur(5px); overflow-y: auto; max-height: 90vh; p { margin: 2px; } } #debugInfo { display: flex; flex-direction: column; } .agentType { margin-bottom: 10px; } .agentType p { margin: 2px 0; } .barContainer { display: flex; flex-direction: column; margin-top: 5px; } .barChart { width: 100%; height: 20px; background: transparent; border-radius: 5px; overflow: hidden; margin-bottom: 5px; } .bar { height: 100%; } .predator-text p { color: #FF4000; } .prey-text p { color: #00BFFF; } .normal-text p { color: #4F4F4F; } .super-predator-text p { color: #FF1493; } .scavenger-text p { color: #8B4513; } .explorer-text p { color: #32CD32; } .builder-text p { color: #FFD700; } .healer-text p { color: #EE82EE; } .predator .bar { background: #FF4000; } .prey .bar { background: #00BFFF; } .normal .bar { background: #4F4F4F; } .super-predator .bar { background: #FF1493; } .scavenger .bar { background: #8B4513; } .explorer .bar { background: #32CD32; } .builder .bar { background: #FFD700; } .healer .bar { background: #EE82EE; } #container div { transition: color 0.5s ease, font-size 0.5s ease; }
console.log("Event Fired") let maze = []; let cols, rows; const charSize = 25; // Smaller size for more detailed maze let agents = []; const populationSize = 80; const mutationRate = 0.1; const maxSteps = 1000; // Maximum number of steps before resetting let generation = 0; const container = document.getElementById("container"); // Define rewards and penalties const rewardGoal = 10; // Reward for reaching the goal const penaltyStep = -0.1; // Penalty for each step taken const penaltyWall = -1; // Penalty for hitting a wall const penaltyBacktrack = -0.5; // Penalty for backtracking let goalX, goalY; // Define goal coordinates class MazeCell { constructor(x, y) { this.x = x; this.y = y; this.walls = [true, true, true, true]; // Top, right, bottom, left this.visited = false; } } function setup() { container.innerHTML = ""; const width = window.innerWidth; const height = window.innerHeight; cols = Math.floor(width / charSize); rows = Math.floor(height / charSize); container.style.gridTemplateColumns = `repeat(${cols}, ${charSize}px)`; container.style.gridTemplateRows = `repeat(${rows}, ${charSize}px)`; maze = []; for (let y = 0; y < rows; y++) { for (let x = 0; x < cols; x++) { maze.push(new MazeCell(x, y)); } } generateMaze(); initializeAgents(); // Set goal coordinates goalX = cols - 1; goalY = rows - 1; } function generateMaze() { const stack = []; const start = maze[0]; start.visited = true; stack.push(start); while (stack.length > 0) { const current = stack[stack.length - 1]; const next = getUnvisitedNeighbor(current); if (next) { next.visited = true; stack.push(next); removeWalls(current, next); // Randomly remove additional walls to make the maze easier if (Math.random() < 0.7) { // 70% chance to remove a random wall const additionalNext = getRandomNeighbor(current); if (additionalNext && additionalNext.visited) { removeWalls(current, additionalNext); } } } else { stack.pop(); } } renderMaze(); } function getUnvisitedNeighbor(cell) { const neighbors = []; const top = maze[index(cell.x, cell.y - 1)]; const right = maze[index(cell.x + 1, cell.y)]; const bottom = maze[index(cell.x, cell.y + 1)]; const left = maze[index(cell.x - 1, cell.y)]; if (top && !top.visited) neighbors.push(top); if (right && !right.visited) neighbors.push(right); if (bottom && !bottom.visited) neighbors.push(bottom); if (left && !left.visited) neighbors.push(left); if (neighbors.length > 0) { const randomIndex = Math.floor(Math.random() * neighbors.length); return neighbors[randomIndex]; } return undefined; } function getRandomNeighbor(cell) { const neighbors = []; const top = maze[index(cell.x, cell.y - 1)]; const right = maze[index(cell.x + 1, cell.y)]; const bottom = maze[index(cell.x, cell.y + 1)]; const left = maze[index(cell.x - 1, cell.y)]; if (top) neighbors.push(top); if (right) neighbors.push(right); if (bottom) neighbors.push(bottom); if (left) neighbors.push(left); if (neighbors.length > 0) { const randomIndex = Math.floor(Math.random() * neighbors.length); return neighbors[randomIndex]; } return undefined; } function index(x, y) { if (x < 0 || y < 0 || x >= cols || y >= rows) { return -1; } return x + y * cols; } function removeWalls(a, b) { const x = a.x - b.x; if (x === 1) { a.walls[3] = false; b.walls[1] = false; } else if (x === -1) { a.walls[1] = false; b.walls[3] = false; } const y = a.y - b.y; if (y === 1) { a.walls[0] = false; b.walls[2] = false; } else if (y === -1) { a.walls[2] = false; b.walls[0] = false; } } function renderMaze() { maze.forEach((cell) => { const element = document.createElement("div"); element.style.width = `${charSize}px`; element.style.height = `${charSize}px`; element.style.display = "flex"; element.style.alignItems = "center"; element.style.justifyContent = "center"; element.style.borderTop = cell.walls[0] ? "1px solid white" : "none"; element.style.borderRight = cell.walls[1] ? "1px solid white" : "none"; element.style.borderBottom = cell.walls[2] ? "1px solid white" : "none"; element.style.borderLeft = cell.walls[3] ? "1px solid white" : "none"; container.appendChild(element); }); } class QLearningAgent { constructor(x, y, baseColorHue = Math.random() * 360) { this.x = x; this.y = y; this.baseColorHue = baseColorHue; this.colorHue = baseColorHue + (Math.random() * 20 - 10); // Slight variation this.steps = 0; this.memory = []; this.qTable = {}; this.learningRate = 0.1; this.discountFactor = 0.9; this.explorationRate = 0.1; this.reachedGoal = false; this.fitness = 0; // Added fitness property } getState() { const current = maze[index(this.x, this.y)]; const distances = [ this.getDistanceToWall(this.x, this.y, -1, 0), // Distance to top wall this.getDistanceToWall(this.x, this.y, 1, 0), // Distance to right wall this.getDistanceToWall(this.x, this.y, 0, 1), // Distance to bottom wall this.getDistanceToWall(this.x, this.y, 0, -1) // Distance to left wall ]; return `${current.walls[0]}${current.walls[1]}${current.walls[2]}${current.walls[3]}-${distances.join('-')}`; } getDistanceToWall(x, y, dx, dy) { let distance = 0; while (x >= 0 && y >= 0 && x < cols && y < rows) { if (maze[index(x, y)].walls[dx + 1] || maze[index(x, y)].walls[dy + 2]) { break; } x += dx; y += dy; distance++; } return distance; } chooseAction() { const state = this.getState(); if (!this.qTable[state] || Math.random() < this.explorationRate) { return Math.floor(Math.random() * 4); // Explore: random action } // Exploit: choose the action with the highest Q-value return Object.keys(this.qTable[state]).reduce((a, b) => this.qTable[state][a] > this.qTable[state][b] ? a : b); } updateQTable(state, action, reward, nextState) { if (!this.qTable[state]) { this.qTable[state] = { 0: 0, 1: 0, 2: 0, 3: 0 }; } if (!this.qTable[nextState]) { this.qTable[nextState] = { 0: 0, 1: 0, 2: 0, 3: 0 }; } const bestNextAction = Object.keys(this.qTable[nextState]).reduce((a, b) => this.qTable[nextState][a] > this.qTable[nextState][b] ? a : b); this.qTable[state][action] = this.qTable[state][action] + this.learningRate * (reward + this.discountFactor * this.qTable[nextState][bestNextAction] - this.qTable[state][action]); } update() { const previousState = this.getState(); const action = this.chooseAction(); const previousX = this.x; const previousY = this.y; if (action == 0 && !maze[index(this.x, this.y)].walls[0]) { this.y--; } else if (action == 1 && !maze[index(this.x, this.y)].walls[1]) { this.x++; } else if (action == 2 && !maze[index(this.x, this.y)].walls[2]) { this.y++; } else if (action == 3 && !maze[index(this.x, this.y)].walls[3]) { this.x--; } this.steps++; let reward = penaltyStep; if (this.x == previousX && this.y == previousY) { reward += penaltyWall; } if (this.memory.some(memory => memory.x == this.x && memory.y == this.y)) { reward += penaltyBacktrack; } if (this.x === goalX && this.y === goalY) { reward += rewardGoal; this.reachedGoal = true; } const nextState = this.getState(); this.updateQTable(previousState, action, reward, nextState); this.memory.push({ x: this.x, y: this.y }); if (this.memory.length > 10) { this.memory.shift(); } // Update fitness based on reward this.fitness += reward; } render(timestamp) { const index = this.y * cols + this.x; const element = container.children[index]; const hue = (timestamp / 50 + this.colorHue) % 360; element.style.backgroundColor = `hsl(${hue}, 100%, 50%)`; } } function initializeAgents() { const baseColorHue = Math.random() * 360; agents = []; for (let i = 0; i < populationSize; i++) { const agent = new QLearningAgent(0, 0, baseColorHue); agents.push(agent); } } function nextGeneration() { const parents = selectParents(); reproduce(parents); } function selectParents() { agents.sort((a, b) => b.fitness - a.fitness); return agents.slice(0, populationSize / 2); } function reproduce(parents) { const newAgents = []; const baseColorHue = parents[0].baseColorHue; // Use base color hue from the best agent while (newAgents.length < populationSize) { const parentA = parents[Math.floor(Math.random() * parents.length)]; const parentB = parents[Math.floor(Math.random() * parents.length)]; const childColorHue = (parentA.colorHue + parentB.colorHue) / 2; const child = new QLearningAgent(0, 0, baseColorHue); child.colorHue = childColorHue; // Set the color hue for the child newAgents.push(child); } agents = newAgents; } function animate(timestamp) { // Update background gradient container.style.background = `black`//linear-gradient(135deg, hsl(${timestamp / 50 % 360}, 50%, 50%) 0%, hsl(${(timestamp / 50 + 180) % 360}, 50%, 50%) 100%)`; // Update wall colors dynamically // maze.forEach((cell, index) => { // const element = container.children[index]; // const row = Math.floor(index / cols); // const col = index % cols; // const hue = (timestamp / 100 + row + col) % 360; // element.style.borderTopColor = cell.walls[0] ? `hsl(${hue}, 50%, 80%)` : "transparent"; // element.style.borderRightColor = cell.walls[1] ? `hsl(${hue}, 50%, 80%)` : "transparent"; // element.style.borderBottomColor = cell.walls[2] ? `hsl(${hue}, 50%, 80%)` : "transparent"; // element.style.borderLeftColor = cell.walls[3] ? `hsl(${hue}, 50%, 80%)` : "transparent"; // }); agents.forEach((agent) => { agent.update(); agent.render(timestamp); }); // Criteria to move to the next generation if (agents.some(agent => agent.reachedGoal) || agents.every(agent => agent.steps >= maxSteps)) { generation++; nextGeneration(); initializeAgents(); } requestAnimationFrame(animate); } setup(); animate();